package com.yy.wga.mp.module.user.manager;

import cn.hutool.core.bean.BeanUtil;
import com.github.pagehelper.PageHelper;
import com.yy.wga.common.core.dto.PageBean;
import com.yy.wga.common.core.dto.Result;
import com.yy.wga.common.core.enmus.EResponseCode;
import com.yy.wga.common.core.exception.BusinessException;
import com.yy.wga.common.util.common.IdUtils;
import com.yy.wga.common.util.common.JsonUtils;
import com.yy.wga.common.util.common.StringUtils;
import com.yy.wga.common.utils.http.HttpContextUtils;
import com.yy.wga.common.utils.http.IPUtils;
import com.yy.wga.common.utils.mapping.BeansUtils;
import com.yy.wga.common.web.intereceptor.RequestHolder;
import com.yy.wga.income.feign.api.IncomeUserBalanceFeign;
import com.yy.wga.income.feign.request.IncomeUserBalanceListRequest;
import com.yy.wga.income.feign.response.IncomeUserBalanceInfoResponse;
import com.yy.wga.mp.common.contants.MpConstant;
import com.yy.wga.mp.common.dto.UserCacheDTO;
import com.yy.wga.mp.common.interceptor.RequestUserHolder;
import com.yy.wga.mp.module.admin.request.AdminUserBasePageRequest;
import com.yy.wga.mp.module.admin.request.AdminUserBaseUpdateStatusRequest;
import com.yy.wga.mp.module.admin.response.AdminUserBasePageResponse;
import com.yy.wga.mp.module.promoter.api.response.bind.GetPromoterRegisterAndBindStatusResp;
import com.yy.wga.mp.module.promoter.service.IPromoterService;
import com.yy.wga.mp.module.third.enums.EAliSMSTemplateCode;
import com.yy.wga.mp.module.third.manager.SmsManager;
import com.yy.wga.mp.module.third.request.SmsRequest;
import com.yy.wga.mp.module.user.constant.RedisConstant;
import com.yy.wga.mp.module.user.dto.WxGetPhoneNumberDTO;
import com.yy.wga.mp.module.user.enums.EUserGrade;
import com.yy.wga.mp.module.user.request.ModifyPhoneNumberRequest;
import com.yy.wga.mp.module.user.request.SaveWxNumRequest;
import com.yy.wga.mp.module.user.request.SendModifyPhoneNumberRequest;
import com.yy.wga.mp.module.user.request.UserRegisterAndBindStatusSetRequest;
import com.yy.wga.mp.module.user.request.UserWechatInfoSetRequest;
import com.yy.wga.mp.module.user.request.VerifyOldPhoneCodeRequest;
import com.yy.wga.mp.module.user.request.WxAuthCode2SessionRequest;
import com.yy.wga.mp.module.user.request.WxGetPhoneNumberRequest;
import com.yy.wga.mp.module.user.request.WxGetUserInfoRequest;
import com.yy.wga.mp.module.user.response.QueryAccountResponse;
import com.yy.wga.mp.module.user.response.UserParentInfoGetResponse;
import com.yy.wga.mp.module.user.response.UserServerWxNumberGetResponse;
import com.yy.wga.mp.module.user.response.WxAuthCode2SessionResponse;
import com.yy.wga.mp.module.user.response.WxGetPhoneNumberResponse;
import com.yy.wga.mp.module.user.response.WxGetUserInfoResponse;
import com.yy.wga.mp.module.user.utils.WxCryptUtil;
import com.yy.wga.user.feign.api.UserBaseFeign;
import com.yy.wga.user.feign.api.UserLoginLogFeign;
import com.yy.wga.user.feign.api.UserRelationFeign;
import com.yy.wga.user.feign.api.UserWechatFeign;
import com.yy.wga.user.feign.request.UserBaseListRequest;
import com.yy.wga.user.feign.request.UserBaseSaveRequest;
import com.yy.wga.user.feign.request.UserLoginLogSaveRequest;
import com.yy.wga.user.feign.request.UserRelationGetParentRequest;
import com.yy.wga.user.feign.request.UserWechatListRequest;
import com.yy.wga.user.feign.request.UserWechatSaveRequest;
import com.yy.wga.user.feign.request.UserWechatUpdateByOpenIdRequest;
import com.yy.wga.user.feign.request.UserWechatUpdateByUserIdRequest;
import com.yy.wga.user.feign.response.UserBaseInfoResponse;
import com.yy.wga.user.feign.response.UserRelationInfoResponse;
import com.yy.wga.user.feign.response.UserWechatInfoResponse;
import com.yy.wga.wechat.feign.api.WechatMpFeign;
import com.yy.wga.wechat.feign.response.WechatMJscode2SessionResp;
import com.yy.wga.wechat.feign.response.WechatOAuth2AccessTokenResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * description
 *
 * @author <a href="mailto:ludezh@dingtalk.com"> ludezh </a>
 * @version 1.0.0   2020/6/5,11:38
 * @since 1.0.0     2020/6/5,11:38
 */
@Slf4j
@Service
public class UserManager {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private UserBaseFeign userBaseFeign;
    @Resource
    private UserLoginLogFeign userLoginLogFeign;
    @Resource
    private UserWechatFeign userWechatFeign;
    @Resource
    private IncomeUserBalanceFeign incomeUserBalanceFeign;
    @Resource
    private SmsManager smsManagerService;
    @Resource
    private UserRelationFeign userRelationFeign;
    @Resource
    private IPromoterService promoterService;
    @Resource
    private WechatMpFeign wechatMpFeign;

    @Value(value = "${spring.profiles.active:}")
    private String profiles;


    /**
     * 小程序登录
     *
     * @param request 微信登录授权信息
     * @return com.hrz.common.base.Result<com.hrz.hrzb.entity.response.WxAuthCode2SessionResponse>
     * @author ludezh
     * @date 2020/6/6 12:54
     * @version 1.0.0
     */
    public WxAuthCode2SessionResponse authCode2Session(WxAuthCode2SessionRequest request) {
        Result<WechatMJscode2SessionResp> userSessionInfo = wechatMpFeign.getUserSessionInfo(request.getCode());
        if (!userSessionInfo.isSuccess()) {
            throw new BusinessException(userSessionInfo.getMessage());
        }
        WechatMJscode2SessionResp session = userSessionInfo.getData();
        if (StringUtils.isNotBlank(session.getSessionKey())) {
            // 注册用户信息，已注册用户则返回手机号
            WxAuthCode2SessionResponse response = saveWechatInfo(session, request);

            // 将数据放入缓存
            UserCacheDTO accountDTO = BeansUtils.map(response, UserCacheDTO.class);
            accountDTO.setToken(IdUtils.uuid());
            accountDTO.setPhone(response.getPhoneNumber());
            accountDTO.setUnionId(session.getUnionid());
            accountDTO.setOpenId(session.getOpenid());
            accountDTO.setSessionKey(session.getSessionKey());
            String tokenKey = MpConstant.USER_INFO + accountDTO.getToken();
            int expire = RedisConstant.ACCOUNT_TOKEN_EXPIRE_MINUTES + RedisConstant.RANDOM.nextInt(120);
            stringRedisTemplate.opsForValue().set(tokenKey, JsonUtils.toJSONString(accountDTO), expire, TimeUnit.MINUTES);
            if (response.getUserId() != null) {
                // 用户id对应的tokenKey缓存，完成登录才缓存
                String userIdKey = RedisConstant.ACCOUNT_TOKEN + response.getUserId();
                String oldTokenKey = stringRedisTemplate.opsForValue().get(userIdKey);
                if (StringUtils.isNotBlank(oldTokenKey)) {
                    stringRedisTemplate.delete(oldTokenKey);
                }
                stringRedisTemplate.opsForValue().set(userIdKey, tokenKey, expire, TimeUnit.MINUTES);
            }

            // 返回登录响应实体
            response.setAccountSecToken(accountDTO.getToken());
            response.setTokenExpire(expire);
            response.setOpenId(session.getOpenid());
            response.setUnionId(session.getUnionid());
            response.setSecKey(session.getSessionKey());
            return response;
        }
        throw new BusinessException(EResponseCode.FAIL);
    }

    /**
     * 微信公众号授权
     *
     * @param request
     * @return
     */
    public WxAuthCode2SessionResponse officialAccountAuthCode2Session(WxAuthCode2SessionRequest request) {
        Result<WechatOAuth2AccessTokenResp> oAuth2AccessTokenResult = wechatMpFeign.getOfficialOAuth2AccessToken(request.getCode());
        if (!oAuth2AccessTokenResult.isSuccess()) {
            throw new BusinessException(oAuth2AccessTokenResult.getMessage());
        }

        WechatOAuth2AccessTokenResp accessToken = oAuth2AccessTokenResult.getData();
        UserWechatListRequest userWechatListRequest = new UserWechatListRequest();
        userWechatListRequest.setUnionId(accessToken.getUnionId());
        Result<List<UserWechatInfoResponse>> wechatInfoResult = userWechatFeign.list(userWechatListRequest);
        if (!wechatInfoResult.isSuccess()) {
            throw new BusinessException("获取用户信息失败");
        }
        List<UserWechatInfoResponse> userWechatList = wechatInfoResult.getData();
        if (userWechatList.isEmpty()) {
            throw new BusinessException("未查询到用户");
        }
        UserWechatInfoResponse wechat = userWechatList.get(0);
        if (wechat == null) {
            throw new BusinessException("用户不存在");
        }
        UserBaseListRequest userBaseListRequest = new UserBaseListRequest();
        userBaseListRequest.setUserId(Collections.singletonList(wechat.getUserId()));
        Result<PageBean<UserBaseInfoResponse>> userBaseResult = userBaseFeign.list(userBaseListRequest);
        if (!userBaseResult.isSuccess()) {
            throw new BusinessException("获取用户信息失败");
        }
        List<UserBaseInfoResponse> userBaseList = userBaseResult.getData().getList();
        if (userBaseList.isEmpty()) {
            throw new BusinessException("未查询到用户");
        }
        UserBaseInfoResponse userBaseDO = userBaseList.get(0);
        if (userBaseDO.getStatus() != 1) {
            throw new BusinessException("用户已禁用或注销，请联系管理员");
        }
        // 注册用户信息，已注册用户则返回手机号
        WxAuthCode2SessionResponse response = new WxAuthCode2SessionResponse();
        response.setCountry(wechat.getCountry());
        response.setProvince(wechat.getProvince());
        response.setCity(wechat.getCity());
        response.setUserId(userBaseDO.getUserId());
        response.setAvatarUrl(userBaseDO.getAvatarUrl());
        response.setSex(userBaseDO.getSex());
        response.setNickName(userBaseDO.getNickName());
        response.setPhoneNumber(userBaseDO.getPhone());
        response.setGrade(userBaseDO.getGrade());
        response.setLevel(userBaseDO.getLevel());
        response.setInviteCode(userBaseDO.getInviteCode());
        response.setWxNum(userBaseDO.getWxNum());
        response.setRegisterBindStatus(userBaseDO.getRegisterBindStatus());

        // 将数据放入缓存
        UserCacheDTO accountDTO = BeansUtils.map(response, UserCacheDTO.class);
        accountDTO.setToken(IdUtils.uuid());
        accountDTO.setPhone(response.getPhoneNumber());
        accountDTO.setUnionId(accessToken.getUnionId());
        accountDTO.setOpenId(accessToken.getOpenId());
        accountDTO.setSessionKey(IdUtils.uuid());
        String tokenKey = MpConstant.USER_INFO + accountDTO.getToken();
        int expire = RedisConstant.ACCOUNT_TOKEN_EXPIRE_MINUTES + RedisConstant.RANDOM.nextInt(120);
        stringRedisTemplate.opsForValue().set(tokenKey, JsonUtils.toJSONString(accountDTO), expire, TimeUnit.MINUTES);
        if (response.getUserId() != null) {
            // 用户id对应的tokenKey缓存，完成登录才缓存
            String userIdKey = RedisConstant.ACCOUNT_TOKEN + response.getUserId();
            String oldTokenKey = stringRedisTemplate.opsForValue().get(userIdKey);
            if (StringUtils.isNotBlank(oldTokenKey)) {
                stringRedisTemplate.delete(oldTokenKey);
            }
            stringRedisTemplate.opsForValue().set(userIdKey, tokenKey, expire, TimeUnit.MINUTES);
        }

        // 返回登录响应实体
        response.setAccountSecToken(accountDTO.getToken());
        response.setTokenExpire(expire);
        response.setOpenId(accessToken.getOpenId());
        response.setUnionId(accessToken.getUnionId());
        response.setSecKey(accountDTO.getSessionKey());
        return response;
    }

    /**
     * 小程序用户信息授权
     *
     * @param request 用户微信信息
     * @return 响应接口
     */
    @Transactional(rollbackFor = Exception.class)
    public WxGetUserInfoResponse getWxUserInfo(WxGetUserInfoRequest request) {
        if (StringUtils.isNotBlank(request.getEncryptedData()) && StringUtils.isNotBlank(request.getIv())) {

            UserCacheDTO accountDTO = RequestUserHolder.getUserCache();
            WxGetUserInfoRequest getUserInfoRequest = new WxGetUserInfoRequest();
            getUserInfoRequest.setIv(request.getIv());
            getUserInfoRequest.setEncryptedData(request.getEncryptedData());
            WxGetUserInfoResponse response = WxCryptUtil.cryptXcxUserInfo(getUserInfoRequest, accountDTO.getSessionKey());

            // 填充响应信息
            response.setUserId(accountDTO.getUserId());
            response.setPhoneNumber(accountDTO.getPhone());
            response.setGrade(accountDTO.getGrade());
            response.setLevel(accountDTO.getLevel());
            response.setInviteCode(accountDTO.getInviteCode());
            response.setOpenId(accountDTO.getOpenId());
            response.setUnionId(accountDTO.getUnionId());

            // 微信信息持久化
            this.saveWechatInfoToDb(response);

            // 更新缓存
            accountDTO.setGender(response.getGender());
            accountDTO.setAvatarUrl(response.getAvatarUrl());
            accountDTO.setUserName(response.getNickName());
            accountDTO.setUserId(response.getUserId());
            accountDTO.setPhone(response.getPhoneNumber());
            accountDTO.setGrade(response.getGrade());
            accountDTO.setLevel(response.getLevel());
            String tokenKey = MpConstant.USER_INFO + accountDTO.getToken();
            int expire = RedisConstant.ACCOUNT_TOKEN_EXPIRE_MINUTES + RedisConstant.RANDOM.nextInt(120);
            stringRedisTemplate.opsForValue().set(tokenKey, JsonUtils.toJSONString(accountDTO), expire, TimeUnit.MINUTES);
            return response;
        }
        throw new BusinessException(EResponseCode.SYSTEM_PARAM_ERROR);
    }

    /**
     * 保存微信信息到数据库
     *
     * @param response 响应实体
     */
    public void saveWechatInfoToDb(WxGetUserInfoResponse response) {
        // 更新数据库微信信息
        UserWechatUpdateByOpenIdRequest updateWechatRequest = new UserWechatUpdateByOpenIdRequest();
        updateWechatRequest.setOpenId(response.getOpenId());
        updateWechatRequest.setAvatarUrl(response.getAvatarUrl());
        updateWechatRequest.setCity(response.getCity());
        updateWechatRequest.setCountry(response.getCountry());
        updateWechatRequest.setProvince(response.getProvince());
        updateWechatRequest.setGender(response.getGender());
        updateWechatRequest.setLanguage(response.getLanguage());
        updateWechatRequest.setWxName(response.getNickName());
        Result<Boolean> updateWechatResult = userWechatFeign.updateByOpenId(updateWechatRequest);
        if (!updateWechatResult.getData()) {
            throw new BusinessException(EResponseCode.SYSTEM_ERROR);
        }

        // 修改用户信息
        UserBaseSaveRequest userBaseSaveRequest = new UserBaseSaveRequest();
        userBaseSaveRequest.setUserId(response.getUserId());
        userBaseSaveRequest.setAvatarUrl(response.getAvatarUrl());
        userBaseSaveRequest.setNickName(response.getNickName());
        userBaseSaveRequest.setSex(response.getGender());
        Result<Long> updateUserResult = userBaseFeign.save(userBaseSaveRequest);
        if (!updateUserResult.isSuccess()) {
            throw new BusinessException(EResponseCode.SYSTEM_ERROR);
        }
    }

    /**
     * 绑定手机号
     *
     * @param request 手机号加密信息
     * @return com.hrz.common.base.Result<com.hrz.hrzb.entity.response.WxGetPhoneNumberResponse>
     * @author ludezh
     * @date 2020/6/6 12:56
     * @version 1.0.0
     */
    @Transactional(rollbackFor = Exception.class)
    public WxGetPhoneNumberResponse authPhoneNumber(WxGetPhoneNumberRequest request) {
        UserCacheDTO userCache = RequestUserHolder.getUserCache();
        WxGetPhoneNumberDTO dto = WxCryptUtil.cryptXcxPhoneNum(request, userCache.getSessionKey());
        if (null != dto && StringUtils.isNotBlank(dto.getPurePhoneNumber())) {
            // 保存用户信息
            WxGetPhoneNumberResponse response = this.saveUserInfo(dto, request);

            // 缓存填充手机号、用户等级信息和邀请码
            userCache.setPhone(dto.getPurePhoneNumber());
            String tokenKey = MpConstant.USER_INFO + userCache.getToken();
            int expire = RedisConstant.ACCOUNT_TOKEN_EXPIRE_MINUTES + RedisConstant.RANDOM.nextInt(120);
            stringRedisTemplate.opsForValue().set(tokenKey, JsonUtils.toJSONString(userCache), expire, TimeUnit.MINUTES);
            return response;
        }
        throw new BusinessException(EResponseCode.SYSTEM_PARAM_ERROR);
    }

    /**
     * 保存微信信息
     *
     * @param dto     微信信息
     * @param request 请求参数
     * @return 是有有微信信息
     */
    private WxAuthCode2SessionResponse saveWechatInfo(WechatMJscode2SessionResp dto, WxAuthCode2SessionRequest request) {
        WxAuthCode2SessionResponse response = new WxAuthCode2SessionResponse();
        // 用户第一次登录，创建用户，否则获取用户信息
        UserWechatListRequest userWechatListRequest = new UserWechatListRequest();
        userWechatListRequest.setOpenId(dto.getOpenid());
        Result<List<UserWechatInfoResponse>> wechatResult = userWechatFeign.list(userWechatListRequest);
        if (!wechatResult.isSuccess()) {
            throw new BusinessException(EResponseCode.SYSTEM_ERROR);
        }
        List<UserWechatInfoResponse> userWechatList = wechatResult.getData();
        if (userWechatList.isEmpty()) {
            Integer userGrade = EUserGrade.COMMON.getType();
            int userLevel = 0;
            // 保存用户信息
            UserBaseSaveRequest saveRequest = new UserBaseSaveRequest();
            saveRequest.setSex(0);
            saveRequest.setNickName("");
            saveRequest.setEmail("");
            saveRequest.setAvatarUrl("");
            saveRequest.setGrade(userGrade);
            saveRequest.setLevel(userLevel);
            Result<Long> saveUserResult = userBaseFeign.save(saveRequest);
            if (!saveUserResult.isSuccess()) {
                throw new BusinessException(EResponseCode.SYSTEM_ERROR);
            }

            // 保存微信信息
            UserWechatSaveRequest wechat = new UserWechatSaveRequest();
            wechat.setUserId(saveUserResult.getData());
            wechat.setOpenId(dto.getOpenid());
            wechat.setUnionId(dto.getUnionid());
            wechat.setGender(0);
            wechat.setWxName("");
            wechat.setAvatarUrl("");
            wechat.setCountry("");
            wechat.setCity("");
            wechat.setProvince("");
            wechat.setLanguage("");
            wechat.setPhone("");
            userWechatFeign.save(wechat);

            response.setUserId(saveUserResult.getData());
            response.setSex(0);
            response.setNickName("");
            response.setAvatarUrl("");
            response.setGrade(userGrade);
            response.setLevel(userLevel);
        } else {
            UserWechatInfoResponse wechat = userWechatList.get(0);
            // 获取用户信息
            UserBaseListRequest userBaseListRequest = new UserBaseListRequest();
            userBaseListRequest.setUserId(Collections.singletonList(wechat.getUserId()));
            Result<PageBean<UserBaseInfoResponse>> userBaseResult = userBaseFeign.list(userBaseListRequest);
            if (!userBaseResult.isSuccess()) {
                throw new BusinessException("获取用户信息失败");
            }
            List<UserBaseInfoResponse> userBaseList = userBaseResult.getData().getList();
            if (userBaseList.isEmpty()) {
                throw new BusinessException("未查询到用户");
            }
            UserBaseInfoResponse userBaseDO = userBaseList.get(0);
            if (userBaseDO.getStatus() != 1) {
                throw new BusinessException("用户已禁用或注销，请联系管理员");
            }

            response.setUserId(userBaseDO.getUserId());
            response.setAvatarUrl(userBaseDO.getAvatarUrl());
            response.setSex(userBaseDO.getSex());
            response.setNickName(userBaseDO.getNickName());
            response.setPhoneNumber(userBaseDO.getPhone());
            response.setGrade(userBaseDO.getGrade());
            response.setLevel(userBaseDO.getLevel());
            response.setInviteCode(userBaseDO.getInviteCode());
            response.setWxNum(userBaseDO.getWxNum());
            response.setRegisterBindStatus(userBaseDO.getRegisterBindStatus());
            response.setCountry(wechat.getCountry());
            response.setProvince(wechat.getProvince());
            response.setCity(wechat.getCity());
        }
        // 异步保存登录日志
        if (StringUtils.isNotBlank(response.getPhoneNumber())) {
            UserLoginLogSaveRequest loginLog = new UserLoginLogSaveRequest();
            loginLog.setAddress(IPUtils.getIpAddr(HttpContextUtils.getHttpServletRequest()));
            loginLog.setPlatform(RequestHolder.getBaseRequest().getSysType());
            loginLog.setOpenId(dto.getOpenid());
            loginLog.setUnionId(dto.getUnionid());
            loginLog.setUserId(response.getUserId());
            userLoginLogFeign.save(loginLog);
        }
        return response;
    }

    /**
     * 保存用户信息
     * 1、判断手机号是否已绑定
     * 2、判断当前账号是否已绑定手机
     * 3、通知php创建用户，数据需要php用户id
     * 4、极速版创建微信信息和用户信息
     * 5、小程序微信信息表绑定手机号和用户id
     * <p>
     * 存在一个风险，php创建用户成功后成功了，极速版创建用户失败，
     * 极速版会通知数据补偿同步旧版用户，用户可能存在短暂数据不一致问题，
     * 后续可以考虑利用mq重试机制来创建红人装用户，保证旧版成功极速版一定成功
     *
     * @param dto     手机信息
     * @param request 登录请求参数
     * @return WxGetPhoneNumberResponse 用户id
     */
    private WxGetPhoneNumberResponse saveUserInfo(WxGetPhoneNumberDTO dto, WxGetPhoneNumberRequest request) {
        // 校验手机号是否已绑定
        // 获取用户信息
        UserBaseListRequest userBaseListRequest = new UserBaseListRequest();
        userBaseListRequest.setPhone(Collections.singletonList(dto.getPhoneNumber()));
        Result<PageBean<UserBaseInfoResponse>> userBaseResult = userBaseFeign.list(userBaseListRequest);
        if (!userBaseResult.isSuccess()) {
            throw new BusinessException("获取用户信息失败");
        }
        List<UserBaseInfoResponse> userBaseList = userBaseResult.getData().getList();
        if (!userBaseList.isEmpty()) {
            throw new BusinessException("手机号已绑定其他账号，请更换其他手机号进行授权");
        }
        // 保存微信信息
        UserCacheDTO userCache = RequestUserHolder.getUserCache();
        UserWechatUpdateByUserIdRequest updateWechatRequest = new UserWechatUpdateByUserIdRequest();
        updateWechatRequest.setUserId(userCache.getUserId());
        updateWechatRequest.setPhone(dto.getPurePhoneNumber());
        Result<Boolean> updateWechatResult = userWechatFeign.updateByUserId(updateWechatRequest);
        if (!updateWechatResult.isSuccess()) {
            throw new BusinessException("保存微信信息失败");
        }

        // 修改用户信息中的手机号信息
        UserBaseSaveRequest saveRequest = new UserBaseSaveRequest();
        saveRequest.setUserId(userCache.getUserId());
        saveRequest.setPhone(dto.getPhoneNumber());
        Result<Long> updateUserResult = userBaseFeign.save(saveRequest);
        if (!updateUserResult.isSuccess()) {
            throw new BusinessException("保存用户信息失败");
        }

        return BeansUtils.map(dto, WxGetPhoneNumberResponse.class);
    }

    /**
     * 更换手机号 - 发送校验原手机号验证码
     */
    public void sendVerifyOldPhoneCode() {
        // 验证码保存缓存
        String code = IdUtils.genRandomNumber(4);
        if (profiles.equals(EProfilesType.DEV.getProfiles()) || profiles.equals(EProfilesType.TEST.getProfiles())) {
            code = "1111";
        }
        UserCacheDTO userCache = RequestUserHolder.getUserCache();
        String redisKey = RedisConstant.VERIFY_PHONE_CODE + userCache.getPhone();
        stringRedisTemplate.opsForValue().set(redisKey, code, 5, TimeUnit.MINUTES);

        // 发送验证码
        if (!profiles.equals(EProfilesType.DEV.getProfiles()) && !profiles.equals(EProfilesType.TEST.getProfiles())) {
            SmsRequest smsRequest = new SmsRequest();
            smsRequest.setParams(code);
            smsRequest.setPhoneList(Collections.singletonList(RequestUserHolder.getUserCache().getPhone()));
            smsRequest.setTemplateCode(EAliSMSTemplateCode.COMMON);
            smsManagerService.send(smsRequest);
        }
    }

    /**
     * 更换手机号 - 校验原手机号验证码
     *
     * @param request 验证码信息
     */
    public void verifyOldPhoneCode(VerifyOldPhoneCodeRequest request) {
        // 校验验证码
        UserCacheDTO userCache = RequestUserHolder.getUserCache();
        String codeRedisKey = RedisConstant.VERIFY_PHONE_CODE + userCache.getPhone();
        String code = stringRedisTemplate.opsForValue().get(codeRedisKey);
        if (!request.getCode().equals(code)) {
            throw new BusinessException(EResponseCode.CAPTCHA_FAIL);
        } else {
            stringRedisTemplate.delete(codeRedisKey);
        }
    }

    /**
     * 退出登录
     *
     * @return 退出登录后的用户信息
     */
    public void logout() {
        UserCacheDTO userCacheDTO = RequestUserHolder.getUserCache();
        String redisKey = MpConstant.USER_INFO + userCacheDTO.getToken();
        stringRedisTemplate.delete(redisKey);
        redisKey = RedisConstant.ACCOUNT_TOKEN + userCacheDTO.getUserId();
        stringRedisTemplate.delete(redisKey);
    }

    /**
     * 发送修改手机号验证码
     *
     * @param request 用户信息
     */
    public void sendModifyPhoneCode(SendModifyPhoneNumberRequest request) {
        // 验证码保存缓存
        String code = IdUtils.genRandomNumber(4);
        if (profiles.equals(EProfilesType.DEV.getProfiles()) || profiles.equals(EProfilesType.TEST.getProfiles())) {
            code = "1111";
        }
        String redisKey = RedisConstant.MODIFY_PHONE_CODE + request.getPhoneNumber();
        stringRedisTemplate.opsForValue().set(redisKey, code, 5, TimeUnit.MINUTES);

        // 发送验证码
        if (!profiles.equals(EProfilesType.DEV.getProfiles()) && !profiles.equals(EProfilesType.TEST.getProfiles())) {
            SmsRequest smsRequest = new SmsRequest();
            smsRequest.setParams(code);
            smsRequest.setPhoneList(Collections.singletonList(RequestUserHolder.getUserCache().getPhone()));
            smsRequest.setTemplateCode(EAliSMSTemplateCode.COMMON);
            smsManagerService.send(smsRequest);
        }
    }


    /**
     * 修改手机号
     *
     * @param request 请求参数
     */
    @Transactional(rollbackFor = Exception.class)
    public void modifyPhoneNumber(ModifyPhoneNumberRequest request) {
        // 校验验证码
        String codeRedisKey = RedisConstant.MODIFY_PHONE_CODE + request.getPhoneNumber();
        String code = stringRedisTemplate.opsForValue().get(codeRedisKey);
        if (!request.getCode().equals(code)) {
            throw new BusinessException(EResponseCode.CAPTCHA_FAIL);
        } else {
            stringRedisTemplate.delete(codeRedisKey);
        }

        // 修改用户信息中的手机号信息
        UserCacheDTO userCache = RequestUserHolder.getUserCache();
        UserBaseSaveRequest saveRequest = new UserBaseSaveRequest();
        saveRequest.setUserId(userCache.getUserId());
        saveRequest.setPhone(request.getPhoneNumber());
        Result<Long> updateUserResult = userBaseFeign.save(saveRequest);
        if (!updateUserResult.isSuccess()) {
            throw new BusinessException("保存用户信息失败");
        }

        // 缓存填充手机号、用户等级信息和邀请码
        userCache.setPhone(request.getPhoneNumber());
        String tokenKey = userCache.getToken();
        int expire = RedisConstant.ACCOUNT_TOKEN_EXPIRE_MINUTES + RedisConstant.RANDOM.nextInt(120);
        stringRedisTemplate.opsForValue().set(tokenKey, JsonUtils.toJSONString(userCache), expire, TimeUnit.MINUTES);
        // 用户id对应的tokenKey缓存
        String userIdKey = String.valueOf(userCache.getUserId());
        stringRedisTemplate.opsForValue().set(userIdKey, tokenKey, expire, TimeUnit.MINUTES);
    }

    /**
     * 保存微信号
     *
     * @param request
     * @return
     */
    public QueryAccountResponse saveWxNum(SaveWxNumRequest request) {
        UserCacheDTO userCache = RequestUserHolder.getUserCache();
        UserBaseSaveRequest saveRequest = new UserBaseSaveRequest();
        saveRequest.setUserId(userCache.getUserId());
        saveRequest.setWxNum(request.getWxNum());
        Result<Long> updateUserResult = userBaseFeign.save(saveRequest);
        if (!updateUserResult.isSuccess()) {
            throw new BusinessException("保存用户信息失败");
        }

        userCache.setWxNum(request.getWxNum());
        //修改缓存
        int expire = RedisConstant.ACCOUNT_TOKEN_EXPIRE_MINUTES + RedisConstant.RANDOM.nextInt(120);
        stringRedisTemplate.opsForValue().set(userCache.getToken(), JsonUtils.toJSONString(userCache), expire, TimeUnit.MINUTES);
        return BeanUtil.copyProperties(userCache, QueryAccountResponse.class);
    }

    /**
     * 分页
     *
     * @param request
     * @return
     */
    public PageBean<AdminUserBasePageResponse> page(AdminUserBasePageRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());

        UserBaseListRequest userListRequest = new UserBaseListRequest();
        userListRequest.setPageNum(request.getPageNum());
        userListRequest.setPageSize(request.getPageSize());
        userListRequest.setCount(Boolean.TRUE);
        BeansUtils.copyProperties(request, userListRequest);
        Result<PageBean<UserBaseInfoResponse>> userInfoResult = userBaseFeign.list(userListRequest);
        if (!userInfoResult.isSuccess()) {
            throw new BusinessException("保存用户信息失败");
        }
        PageBean<UserBaseInfoResponse> userInfoPageBean = userInfoResult.getData();
        List<UserBaseInfoResponse> userBaseList = userInfoPageBean.getList();

        // 查询余额
        IncomeUserBalanceListRequest incomeUserBalanceListRequest = new IncomeUserBalanceListRequest();
        incomeUserBalanceListRequest.setUserIds(userBaseList.stream().map(UserBaseInfoResponse::getUserId)
                .collect(Collectors.toList()));
        Result<List<IncomeUserBalanceInfoResponse>> userBalanceResult = incomeUserBalanceFeign.listByUserId(incomeUserBalanceListRequest);
        if (!userBalanceResult.isSuccess()) {
            throw new BusinessException(EResponseCode.SYSTEM_BUSY_ERROR);
        }
        List<IncomeUserBalanceInfoResponse> userBalanceList = userBalanceResult.getData();
        Map<Long, IncomeUserBalanceInfoResponse> userBalanceMap = userBalanceList.stream()
                .collect(Collectors.toMap(IncomeUserBalanceInfoResponse::getUserId, Function.identity()));


        List<AdminUserBasePageResponse> responseList = BeansUtils.mapList(userBaseList, AdminUserBasePageResponse.class);
        for (AdminUserBasePageResponse response : responseList) {
            IncomeUserBalanceInfoResponse userBalanceDO = userBalanceMap.get(response.getId());
            if (userBalanceDO != null) {
                response.setBalance(userBalanceDO.getSettle());
            }
            if (StringUtils.isNotBlank(response.getProxyCounty())) {
                response.setProxyType(3);
            } else if (StringUtils.isNotBlank(response.getProxyCity())) {
                response.setProxyType(2);
            } else {
                response.setProxyType(1);
            }
        }
        PageBean<AdminUserBasePageResponse> pageBean = BeansUtils.map(userInfoPageBean, PageBean.class);
        pageBean.setList(responseList);
        return pageBean;
    }

    /**
     * 修改用户状态
     *
     * @param request
     */
    public void updateStatus(AdminUserBaseUpdateStatusRequest request) {
        UserBaseSaveRequest userBaseDO = new UserBaseSaveRequest();
        userBaseDO.setUserId(request.getId());
        userBaseDO.setStatus(request.getStatus());
        userBaseFeign.save(userBaseDO);

        // 禁用踢出登录
        if (request.getStatus() == 2) {
            String userIdKey = RedisConstant.ACCOUNT_TOKEN + request.getId();
            String tokenKey = stringRedisTemplate.opsForValue().get(userIdKey);
            if (StringUtils.isNotBlank(tokenKey)) {
                stringRedisTemplate.delete(userIdKey);
                stringRedisTemplate.delete(tokenKey);
            }
        }
    }

    /**
     * 获取服务商微信号
     *
     * @return
     */
    public UserServerWxNumberGetResponse getServerWxNum() {
        Long userId = RequestUserHolder.getUserCache().getUserId();
        while (true) {
            UserRelationGetParentRequest getParentRequest = new UserRelationGetParentRequest();
            getParentRequest.setUserId(userId);
            Result<UserRelationInfoResponse> userRelationResult = userRelationFeign.getParent(getParentRequest);
            if (!userRelationResult.isSuccess()) {
                throw new BusinessException("获取用户关系失败");
            }
            UserRelationInfoResponse userRelation = userRelationResult.getData();
            if (userRelation == null) {
                return null;
            }
            if (userRelation.getParentGrade().equals(EUserGrade.SERVER_1.getType())) {
                UserBaseListRequest userListRequest = new UserBaseListRequest();
                userListRequest.setUserId(Collections.singletonList(userRelation.getParentUserId()));
                Result<PageBean<UserBaseInfoResponse>> userInfoResult = userBaseFeign.list(userListRequest);
                if (!userInfoResult.isSuccess()) {
                    throw new BusinessException("查询用户信息失败");
                }
                List<UserBaseInfoResponse> userBaseInfoList = userInfoResult.getData().getList();
                UserServerWxNumberGetResponse response = new UserServerWxNumberGetResponse();
                response.setWxNum(userBaseInfoList.get(0).getWxNum());
                return response;
            }
            userId = userRelation.getParentUserId();
        }
    }

    /**
     * 获取上级信息
     *
     * @return
     */
    public UserParentInfoGetResponse getParentInfo() {
        UserParentInfoGetResponse response = new UserParentInfoGetResponse();
        Long userId = RequestUserHolder.getUserCache().getUserId();
        while (true) {
            UserRelationGetParentRequest getParentRequest = new UserRelationGetParentRequest();
            getParentRequest.setUserId(userId);
            Result<UserRelationInfoResponse> userRelationResult = userRelationFeign.getParent(getParentRequest);
            if (!userRelationResult.isSuccess()) {
                throw new BusinessException("获取用户关系失败");
            }
            UserRelationInfoResponse userRelation = userRelationResult.getData();
            if (userRelation == null) {
                break;
            }
            if (userRelation.getParentGrade().equals(EUserGrade.SERVER_1.getType())) {
                UserBaseListRequest userListRequest = new UserBaseListRequest();
                userListRequest.setUserId(Collections.singletonList(userRelation.getParentUserId()));
                Result<PageBean<UserBaseInfoResponse>> userInfoResult = userBaseFeign.list(userListRequest);
                if (!userInfoResult.isSuccess()) {
                    throw new BusinessException("查询用户信息失败");
                }
                response.setServerInfo(BeansUtils.map(userInfoResult.getData().getList().get(0), UserParentInfoGetResponse.UserInfo.class));
                if (response.getParentInfo() == null) {
                    response.setParentInfo(response.getServerInfo());
                }
                return response;
            }
            if (response.getParentInfo() == null) {UserBaseListRequest userListRequest = new UserBaseListRequest();
                userListRequest.setUserId(Collections.singletonList(userRelation.getParentUserId()));
                Result<PageBean<UserBaseInfoResponse>> userInfoResult = userBaseFeign.list(userListRequest);
                if (!userInfoResult.isSuccess()) {
                    throw new BusinessException("查询用户信息失败");
                }
                response.setParentInfo(BeansUtils.map(userInfoResult.getData().getList().get(0), UserParentInfoGetResponse.UserInfo.class));
            }
            userId = userRelation.getParentUserId();
        }
        return response;
    }

    public QueryAccountResponse setRegisterBindStatus(UserRegisterAndBindStatusSetRequest request) {
        UserCacheDTO userCache = RequestUserHolder.getUserCache();
        UserBaseSaveRequest userBaseDO = new UserBaseSaveRequest();
        userBaseDO.setUserId(userCache.getUserId());
        userBaseDO.setRegisterBindStatus(request.getRegisterBindStatus());
        userBaseFeign.save(userBaseDO);


        UserWechatListRequest userWechatListRequest = new UserWechatListRequest();
        userWechatListRequest.setUserId(Collections.singletonList(userCache.getUserId()));
        Result<List<UserWechatInfoResponse>> userWechatResult = userWechatFeign.list(userWechatListRequest);
        if (!userWechatResult.isSuccess()) {
            throw new BusinessException(EResponseCode.SYSTEM_BUSY_ERROR);
        }
        List<UserWechatInfoResponse> userWechatList = userWechatResult.getData();
        if (userWechatList.isEmpty()) {
            throw new BusinessException(EResponseCode.SYSTEM_BUSY_ERROR);
        }
        UserWechatInfoResponse userWechat = userWechatList.get(0);
        if (Objects.nonNull(userWechat) && StringUtils.isBlank(userWechat.getSharerAppid())) {
            GetPromoterRegisterAndBindStatusResp statusResp = promoterService.getPromoterRegisterAndBindStatus(userCache.getOpenId());
            String sharerAppid = statusResp.getSharerAppid();
            UserWechatSaveRequest userWechatSaveRequest = new UserWechatSaveRequest();
            userWechatSaveRequest.setSharerAppid(sharerAppid);
            userWechatSaveRequest.setUserId(userCache.getUserId());
            Result<Boolean> saveWechatResult = userWechatFeign.save(userWechatSaveRequest);
            if (!saveWechatResult.isSuccess()) {
                throw new BusinessException(EResponseCode.SYSTEM_BUSY_ERROR);
            }
        }


        userCache.setRegisterBindStatus(request.getRegisterBindStatus());
        //修改缓存
        int expire = RedisConstant.ACCOUNT_TOKEN_EXPIRE_MINUTES + RedisConstant.RANDOM.nextInt(120);
        stringRedisTemplate.opsForValue().set(userCache.getToken(), JsonUtils.toJSONString(userCache), expire, TimeUnit.MINUTES);
        return BeanUtil.copyProperties(userCache, QueryAccountResponse.class);
    }

    public QueryAccountResponse setUserWechatInfo(UserWechatInfoSetRequest request) {
        UserCacheDTO userCache = RequestUserHolder.getUserCache();
        UserWechatSaveRequest userWechatSaveRequest = new UserWechatSaveRequest();
        userWechatSaveRequest.setUserId(userCache.getUserId());
        if (StringUtils.isNotBlank(request.getAvatarUrl())) {
            userWechatSaveRequest.setAvatarUrl(request.getAvatarUrl());
            userCache.setAvatarUrl(request.getAvatarUrl());
        }
        if (StringUtils.isNotBlank(request.getWxName())) {
            userWechatSaveRequest.setWxName(request.getWxName());
            userCache.setUserName(request.getAvatarUrl());
        }
        userWechatFeign.save(userWechatSaveRequest);
        //修改缓存
        int expire = RedisConstant.ACCOUNT_TOKEN_EXPIRE_MINUTES + RedisConstant.RANDOM.nextInt(120);
        stringRedisTemplate.opsForValue().set(userCache.getToken(), JsonUtils.toJSONString(userCache), expire, TimeUnit.MINUTES);
        return BeanUtil.copyProperties(userCache, QueryAccountResponse.class);
    }
}
